package net.wanderinghorse.jsonmessage;

import net.wanderinghorse.json.JSONException;

import java.io.*;
import java.net.*;
import java.util.Map;

/**
 *  A collection of utility functions specifically dealing with fetching JSON data over
 *  a network.
 */
public class NetUtil {

    /**
     * Opens a connection to a URL in read/write mode, for subsequent posting of data
     * and reading the response.
     *
     * @param url The remote URL to prepare a POST connection to.
     * @param timeout Timeout time, in milliseconds, for establishing a connection connection and reading input.
     * Set to 0 for no timeout. Negative values are ignored.
     * @param proxy Optional connection proxy. May be null.
     * @param doConnect If true, this function calls connect() on the connection, else it does not. Callers who want
     * to set certain options may have to pass false here and set those options before calling connect().
     * @return On success, an opened URLConnection. On error it throws.
     * @throws MalformedURLException If the given URL is malformed.
     * @throws IOException If connection fails.
     */
    public static URLConnection prepareUrlPost( final String url, final int timeout, final Proxy proxy, final boolean doConnect )
    throws MalformedURLException, IOException {
       /*
        TODO: see if we can re-implement this using sun.www.http.HttpClient,
        with the hope being that we could then implement cookie support so we
        can store remote session IDs and whatnot.
       */
        final URL where = new URL( url );
        /**
         Reminders to self...

         Proxy p = new Proxy( Proxy.HTTP, new InetSocketAddress( PROXY_HOST, PROXY_PORT ) );
        */
        URLConnection con = (null == proxy)
            ? where.openConnection()
            : where.openConnection( proxy );
        con.setDoOutput( true );
        if( 0 <= timeout ) {
            con.setConnectTimeout( timeout );
            con.setReadTimeout( timeout );
        }
        if( doConnect ) {
            con.connect();
        }
        return con;
    }

    /**
     * Tries to fetch JSONMessage response data from a remote URL. The URL is assumed to be a JSONMessage request
     * dispatcher which responds with JSONMessage response objects.
     *
     * If useEncodedForm then the ata is sent URL-encoded in the form:
     *
     *  Request.REQUEST_KEY={JSON-encoded form of the r object}
     *
     * (Where Request.REQUEST_KEY is a framework-defined constant.)
     *
     * If useEncodedForm is false then r.toJSON() is posted directly as plain text, which is much more efficient.
     *
     * @param r The request object to send to the URL.
     * @param opt The post options. Only getUrl() and getTimeout() are used by this function.
     * @param proxy An optional HTTP proxy (may be null).
     * @param useEncodedForm If true, URL-encode the request, else stream it as plain text.
     * @return The fetch data, which will _presumably_ be a well-formed JSONMessage Response.
     * @throws MalformedURLException If opt.getUrl() is malformed.
     * @throws IOException If any network operations throw.
     */
    public static String fetchResponseJSON( final Request r, final PostOptions opt, final Proxy proxy, final boolean useEncodedForm )
    throws MalformedURLException, IOException {

        final Integer ti = opt.getTimeout();
        URLConnection con = prepareUrlPost( opt.getUrl(),(null==ti)?0:ti, proxy, false );
        con.setRequestProperty("Content-Type", useEncodedForm ? "application/x-www-form-urlencoded" : "text/plain");
        con.connect();
        OutputStreamWriter wri = new OutputStreamWriter(con.getOutputStream());
//                final String jp = opt.getJsonpCallback();
//                if( null != jp ) {
//                    wri.write( Request.JSONP_KEY+"="+jp+'&' );
//                }
        if( useEncodedForm ) {
            wri.write( Request.REQUEST_KEY+"=" );
            wri.write( URLEncoder.encode( r.toJSON(), "UTF-8" ) );
        }
        else {
            wri.write( r.toJSON() );
        }
        wri.flush();
        try { wri.close(); } catch( Exception ex ) {/*ignore*/}
        StringBuilder json = null;
        BufferedReader rd = new BufferedReader( new InputStreamReader( con.getInputStream() ) );
        {
            json = new StringBuilder();
            String line;
            while( null != (line = rd.readLine()) ) {
                json.append(line);
            }
        }
        try { rd.close(); } catch( Exception ex ) {/*ignore*/}
        return json.toString();
    }

    /**
     * Fetches data using fetchResponseJSON() and then tries to construct a Response object based
     * on that data.
     *
     * @param req The request object to send to the URL.
     * @param opt The post options. See fetchResponseJSON() for details.
     * @param proxy An optional HTTP proxy (may be null).
     * @return The fetched response data.
     * @throws MalformedURLException If opt.getUrl() is malformed.
     * @throws IOException If any network operations throw.
     * @throws JSONException If parsing of the JSON data fails. No semantic validation is done here - only
     * syntactical validation of the JSON.
     */
    public static Response fetchResponse( final Request req, final PostOptions opt, final Proxy proxy )
    throws MalformedURLException, IOException, JSONException {
        final String json = fetchResponseJSON( req, opt, proxy, false );
        Response res = new Response();
        res.fromJSON(json);
        return res;
    }
}
